package com.starhub.utils;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

/**
 * DTO转Map工具类
 * 使用反射将DTO对象的所有字段转换为Map对象
 * 
 * @version: V1.0
 * @author: liuhf
 * @time 2025-06-18
 */
@Slf4j
public class DtoToMapConverter {

    /**
     * 将DTO对象转换为Map
     * 
     * @param dto DTO对象
     * @return Map对象，key为字段名，value为字段值
     */
    public static Map<String, Object> convert(Object dto) {
        return convert(dto, false);
    }

    /**
     * 将DTO对象转换为Map
     * 
     * @param dto DTO对象
     * @return Map对象，key为字段名，value为字段值
     */
    public static Map<String, Object> convertWithNull(Object dto) {
        return convert(dto, true);
    }
    
    /**
     * 将DTO对象转换为Map
     * 
     * @param dto DTO对象
     * @param includeNull 是否包含null值
     * @return Map对象，key为字段名，value为字段值
     */
    public static Map<String, Object> convert(Object dto, boolean includeNull) {
        if (dto == null) {
            return new HashMap<>();
        }

        Map<String, Object> result = new HashMap<>();
        Class<?> clazz = dto.getClass();

        try {
            // 获取所有字段（包括私有字段）
            Field[] fields = getAllFields(clazz);
            
            for (Field field : fields) {
                // 跳过静态字段和final字段
                if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
                    continue;
                }

                // 设置字段可访问
                field.setAccessible(true);
                
                try {
                    Object value = field.get(dto);
                    
                    // 根据includeNull参数决定是否包含null值
                    if (value != null || includeNull) {
                        result.put(field.getName(), value);
                    }
                } catch (IllegalAccessException e) {
                    log.warn("Failed to access field: {}", field.getName(), e);
                }
            }
        } catch (Exception e) {
            log.error("Failed to convert DTO to Map: {}", dto.getClass().getSimpleName(), e);
        }

        return result;
    }

    /**
     * 将DTO对象转换为Map，排除指定字段
     * 
     * @param dto DTO对象
     * @param excludeFields 要排除的字段名数组
     * @return Map对象
     */
    public static Map<String, Object> convertExclude(Object dto, String... excludeFields) {
        Map<String, Object> result = convert(dto);
        
        if (excludeFields != null) {
            for (String fieldName : excludeFields) {
                result.remove(fieldName);
            }
        }
        
        return result;
    }

    /**
     * 将DTO对象转换为Map，只包含指定字段
     * 
     * @param dto DTO对象
     * @param includeFields 要包含的字段名数组
     * @return Map对象
     */
    public static Map<String, Object> convertInclude(Object dto, String... includeFields) {
        if (dto == null || includeFields == null || includeFields.length == 0) {
            return new HashMap<>();
        }

        Map<String, Object> result = new HashMap<>();
        Class<?> clazz = dto.getClass();

        try {
            Field[] fields = getAllFields(clazz);
            
            for (Field field : fields) {
                if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
                    continue;
                }

                // 检查字段是否在包含列表中
                boolean shouldInclude = false;
                for (String includeField : includeFields) {
                    if (field.getName().equals(includeField)) {
                        shouldInclude = true;
                        break;
                    }
                }

                if (shouldInclude) {
                    field.setAccessible(true);
                    try {
                        Object value = field.get(dto);
                        result.put(field.getName(), value);
                    } catch (IllegalAccessException e) {
                        log.warn("Failed to access field: {}", field.getName(), e);
                    }
                }
            }
        } catch (Exception e) {
            log.error("Failed to convert DTO to Map: {}", dto.getClass().getSimpleName(), e);
        }

        return result;
    }

    /**
     * 获取类的所有字段（包括父类字段）
     * 
     * @param clazz 类
     * @return 字段数组
     */
    private static Field[] getAllFields(Class<?> clazz) {
        Map<String, Field> fieldMap = new HashMap<>();
        
        // 遍历类的继承层次
        while (clazz != null && clazz != Object.class) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                // 如果子类没有重写该字段，则添加
                if (!fieldMap.containsKey(field.getName())) {
                    fieldMap.put(field.getName(), field);
                }
            }
            clazz = clazz.getSuperclass();
        }
        
        return fieldMap.values().toArray(new Field[0]);
    }

    /**
     * 将Map转换为指定类型的对象
     * 
     * @param map Map对象
     * @param targetClass 目标类型
     * @param <T> 泛型类型
     * @return 目标对象
     */
    public static <T> T convertToDto(Map<String, Object> map, Class<T> targetClass) {
        if (map == null || targetClass == null) {
            return null;
        }

        try {
            T instance = targetClass.getDeclaredConstructor().newInstance();
            Field[] fields = getAllFields(targetClass);

            for (Field field : fields) {
                if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
                    continue;
                }

                String fieldName = field.getName();
                if (map.containsKey(fieldName)) {
                    field.setAccessible(true);
                    Object value = map.get(fieldName);
                    
                    // 类型转换
                    if (value != null) {
                        try {
                            if (field.getType().isAssignableFrom(value.getClass())) {
                                field.set(instance, value);
                            } else {
                                // 基本类型转换
                                setFieldValue(instance, field, value);
                            }
                        } catch (Exception e) {
                            log.warn("Failed to set field: {} with value: {}", fieldName, value, e);
                        }
                    }
                }
            }

            return instance;
        } catch (Exception e) {
            log.error("Failed to convert Map to DTO: {}", targetClass.getSimpleName(), e);
            return null;
        }
    }

    /**
     * 设置字段值，处理基本类型转换
     * 
     * @param instance 实例对象
     * @param field 字段
     * @param value 值
     */
    private static void setFieldValue(Object instance, Field field, Object value) throws IllegalAccessException {
        Class<?> fieldType = field.getType();
        
        if (fieldType == String.class) {
            field.set(instance, value.toString());
        } else if (fieldType == Integer.class || fieldType == int.class) {
            if (value instanceof Number) {
                field.set(instance, ((Number) value).intValue());
            } else {
                field.set(instance, Integer.parseInt(value.toString()));
            }
        } else if (fieldType == Long.class || fieldType == long.class) {
            if (value instanceof Number) {
                field.set(instance, ((Number) value).longValue());
            } else {
                field.set(instance, Long.parseLong(value.toString()));
            }
        } else if (fieldType == Double.class || fieldType == double.class) {
            if (value instanceof Number) {
                field.set(instance, ((Number) value).doubleValue());
            } else {
                field.set(instance, Double.parseDouble(value.toString()));
            }
        } else if (fieldType == Float.class || fieldType == float.class) {
            if (value instanceof Number) {
                field.set(instance, ((Number) value).floatValue());
            } else {
                field.set(instance, Float.parseFloat(value.toString()));
            }
        } else if (fieldType == Boolean.class || fieldType == boolean.class) {
            if (value instanceof Boolean) {
                field.set(instance, value);
            } else {
                field.set(instance, Boolean.parseBoolean(value.toString()));
            }
        } else {
            // 其他类型直接设置
            field.set(instance, value);
        }
    }
} 